package com.lzh.persevere.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lzh.persevere.mapper.*;
import com.lzh.persevere.model.entity.Menu;
import com.lzh.persevere.model.entity.Role;
import com.lzh.persevere.model.entity.RoleMenu;
import com.lzh.persevere.model.qo.PageModelQO;
import com.lzh.persevere.model.ro.role.EditRoleRO;
import com.lzh.persevere.model.vo.role.RoleMenuInfoVO;
import com.lzh.persevere.model.vo.role.RoleMenuVO;
import com.lzh.persevere.service.RoleService;
import com.lzh.persevere.template.PageAbstractTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author luzhiheng
 * @since 2023-12-28
 */
@Service("roleService")
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements RoleService {

    @Resource
    private RedisCacheService redisCacheService;

    @Resource
    private UserRoleMapper userRoleMapper;

    @Resource
    private UserMapper userMapper;

    @Resource
    private MenuMapper menuMapper;

    @Resource
    private RoleMenuMapper roleMenuMapper;

    @Override
    public Map<String, Object> pageList(PageModelQO qo) {
        Map<String, Object> otherQueryMap = new HashMap<>();
        PageAbstractTemplate template = new PageAbstractTemplate() {
            @Override
            public Integer countTotal() {
                return baseMapper.countList(getQueryMap(qo.getPageModel(), otherQueryMap));
            }

            @Override
            public List<Map<String, Object>> queryPageList() {
                List<Map<String, Object>> pageList = baseMapper.getPageList(queryMap);
                Map<String, String> allUserIdNickNameMap = redisCacheService.getAllUserIdNickNameMap();
                if (CollectionUtil.isNotEmpty(pageList)) {
                    pageList.forEach(map -> {
                        map.put("createNickName", allUserIdNickNameMap.get(String.valueOf(map.get("createUserId"))));
                        map.put("updateNickName", allUserIdNickNameMap.get(String.valueOf(map.get("updateUserId"))));
                    });
                }
                return pageList;
            }
        };
        return template.queryPage();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean editRole(EditRoleRO ro) {
        Role role = baseMapper.selectById(ro.getId());
        if (role == null) {
            return false;
        }
        role.setRoleName(ro.getRoleName());
        role.setRoleKey(ro.getRoleKey());
        role.setStatus(ro.getStatus());
        role.setRemark(ro.getRemark());
        role.setRoleSort(ro.getRoleSort());
        baseMapper.updateById(role);
        List<String> selectedMenuKeyList = ro.getSelectedMenuKeyList();
        roleMenuMapper.deleteByRoleId(role.getId());
        if (CollectionUtil.isNotEmpty(selectedMenuKeyList)) {
            List<RoleMenu> roleMenuList = menuMapper.selectList(Wrappers.<Menu>lambdaQuery().in(Menu::getPerms, selectedMenuKeyList))
                    .stream().map(e -> new RoleMenu().setRoleId(role.getId()).setMenuId(e.getId())).collect(Collectors.toList());
            roleMenuMapper.insertBatch(roleMenuList);
        }
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean deleteRole(Long id) {
        baseMapper.deleteById(id);
        userRoleMapper.deleteByRoleId(id);
        return true;
    }

    @Override
    public RoleMenuInfoVO findRoleMenuList(Long id) {
        RoleMenuInfoVO vo = new RoleMenuInfoVO();
        vo.setMenuVOList(findMenuList());
        List<RoleMenu> roleMenuList = roleMenuMapper.selectList(Wrappers.<RoleMenu>lambdaQuery().eq(RoleMenu::getRoleId, id));
        if (CollectionUtil.isEmpty(roleMenuList)) {
            return vo;
        }
        List<Long> menuIdList = roleMenuList.stream().map(RoleMenu::getMenuId).collect(Collectors.toList());
        List<Menu> selectMenuList = menuMapper.selectList(Wrappers.<Menu>lambdaQuery().in(Menu::getId, menuIdList));
        vo.setSelectMenuList(selectMenuList);
        return vo;
    }

    public List<RoleMenuVO> findMenuList() {
        List<Menu> menuList = menuMapper.selectList(null);
        List<RoleMenuVO> menuVOList = menuList.stream().map(e -> {
            RoleMenuVO vo = new RoleMenuVO();
            vo.setTitle(e.getMenuName());
            vo.setKey(e.getPerms());
            vo.setParentId(e.getParentId());
            vo.setId(e.getId());
            return vo;
        }).collect(Collectors.toList());

        List<RoleMenuVO> parentList = menuVOList.stream().filter(e -> e.getParentId() == null || e.getParentId() == -1).collect(Collectors.toList());
        List<RoleMenuVO> subList = menuVOList.stream().filter(e -> e.getParentId() != null && e.getParentId() != -1).collect(Collectors.toList());
        for (RoleMenuVO menuVO : parentList) {
            buildTree(menuVO, subList);
        }
        return parentList;
    }

    private void buildTree(RoleMenuVO parent, List<RoleMenuVO> subList) {
        if (CollectionUtil.isEmpty(subList) || parent == null) {
            return;
        }
        List<RoleMenuVO> list = subList.stream().filter(e -> Objects.equals(e.getParentId(), parent.getId())).collect(Collectors.toList());
        List<RoleMenuVO> list1 = subList.stream().filter(e -> !Objects.equals(e.getParentId(), parent.getId())).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(list)) {
            parent.setChildren(list);
        }
        for (RoleMenuVO sub : list) {
            buildTree(sub, list1);
        }
    }
}
